与普通函数一样，函数模板也可以重载。可以使用相同的函数名来声明不同的函数体，当使用该函数名称时，编译器会决定调用哪一个候选函数。即使没有模板，这个决策规则也会相当复杂。本节中，将讨论涉及模板的重载。如果不熟悉无模板重载的基本规则，请参阅附录C，其中提供了关于重载解析规则相当详细的介绍。

下面的程序示例演示了如何重载函数模板:

\hspace*{\fill} \\ %插入空行
\noindent
\textit{basics/max2.cpp}
\begin{lstlisting}[style=styleCXX]
// maximum of two int values:
int max (int a, int b)
{
	return b < a ? a : b;
}

// maximum of two values of any type:
template<typename T>
T max (T a, T b)
{
	return b < a ? a : b;
}

int main()
{
	::max(7, 42); // calls the nontemplate for two ints
	::max(7.0, 42.0); // calls max<double> (by argument deduction)
	::max('a', 'b'); // calls max<char> (by argument deduction)
	::max<>(7, 42); // calls max<int> (by argument deduction)
	::max<double>(7, 42); // calls max<double> (no argument deduction)
	::max('a', 42.7); // calls the nontemplate for two ints
}
\end{lstlisting}

非模板函数可以与相同名称和相同类型的函数模板共存。其他相同的情况下，重载解析将优先使用非模板方式。第一个调用验证了这个规则:

\begin{lstlisting}[style=styleCXX]
::max(7, 42); // 两个int值完全匹配非模板函数
\end{lstlisting}

若模板可以生成匹配更好的函数实例，则选择模板。这在\texttt{max()}的第二次和第三次调用中得到了验证:

\begin{lstlisting}[style=styleCXX]
::max(7.0, 42.0); // 调用max<double>(通过参数推导)
::max('a', 'b'); // 调用max<char>(通过参数推推导)
\end{lstlisting}

这里，因为不需要将double或char转换成int(参见C.2节的重载解析规则)，模板匹的更好。

也可以显式指定一个空的模板参数列表。这种语法表明只有模板才能解析调用，但所有模板参数都应该根据调用参数进行推导:

\begin{lstlisting}[style=styleCXX]
::max<>(7, 42); // 调用max<int>(通过参数推导)
\end{lstlisting}

对推导的模板参数不进行自动类型转换，从而会自动的对普通函数参数进行类型转换，因此最后一次调用使用了非模板函数('a'和42.7都转换为int):

\begin{lstlisting}[style=styleCXX]
::max('a', 42.7); // 只有非模板函数允许这种类型转换
\end{lstlisting}

一个有趣的例子是重载maximum模板，其只能显式指定返回类型:

\hspace*{\fill} \\ %插入空行
\noindent
\textit{basics/maxdefault4.hpp}
\begin{lstlisting}[style=styleCXX]
template<typename T1, typename T2>
auto max (T1 a, T2 b)
{
	return b < a ? a : b;
}

template<typename RT, typename T1, typename T2>
RT max (T1 a, T2 b)
{
	return b < a ? a : b;
}
\end{lstlisting}

现在，可以调用max():

\begin{lstlisting}[style=styleCXX]
auto a = ::max(4, 7.2); // 使用第一个函数模板
auto b = ::max<long double>(7.2, 4); // 使用第二个函数模板
\end{lstlisting}

当这样使用时:

\begin{lstlisting}[style=styleCXX]
auto c = ::max<int>(4, 7.2); // ERROR: 两个函数模板都可以匹配
\end{lstlisting}

这两个模板都匹配，这导致重载解析过程出现歧义。因此，重载函数模板时，应该确保只有一个函数模板与调用匹配。

例子是重载指针和普通C字符串的最大值比较函数模板:

\hspace*{\fill} \\ %插入空行
\noindent
\textit{basics/max3val.cpp}
\begin{lstlisting}[style=styleCXX]
#include <cstring>
#include <string>

// 任意类型的两个最大值:
template<typename T>
T max (T a, T b)
{
	return b < a ? a : b;
}

// 两个指针内容的最大值:
template<typename T>
T* max (T* a, T* b)
{
	return *b < *a ? a : b;
}

// 两个C字符串的最大值:
char const* max (char const* a, char const* b)
{
	return std::strcmp(b,a) < 0 ? a : b;
}

int main ()
{
	int a = 7;
	int b = 42;
	auto m1 = ::max(a,b); // max():两个int类型的值
	
	std::string s1 = "hey";
	std::string s2 = "you";
	auto m2 = ::max(s1,s2); // max():两个std::string类型的值
	
	int* p1 = &b;
	int* p2 = &a;
	auto m3 = ::max(p1,p2); // max():两个指针类型的值
	
	char const* x = "hello";
	char const* y = "world";
	auto m4 = ::max(x,y); // max():两个C字符串类型的值
}
\end{lstlisting}

注意，max()的所有重载中，参数都是通过值传递的。重载函数模板时，最好不要进行不必要的更改，可以将更改限制在参数的数量或显式地指定模板参数。否则，可能会出现意想不到的后果。若实现了max()模板，通过引用传递参数，对两个通过值传递C字符串进行重载实现，从而就不能使用三个参数的版本来比较三个C字符串:

\hspace*{\fill} \\ %插入空行
\noindent
\textit{basics/max3ref.cpp}
\begin{lstlisting}[style=styleCXX]
#include <cstring>

// 任意类型的两个最大值(引用调用)
template<typename T>
T const& max (T const& a, T const& b)
{
	return b < a ? a : b;
}

// 最多两个C字符串(值调用)
char const* max (char const* a, char const* b)
{
	return std::strcmp(b,a) < 0 ? a : b;
}

// 任何类型的最多三个值(引用调用)
template<typename T>
T const& max (T const& a, T const& b, T const& c)
{
	return max (max(a,b), c); // 如果max(a,b)使用了按值调用就会出错
}

int main ()
{
	auto m1 = ::max(7, 42, 68); // OK
	
	char const* s1 = "frederic";
	char const* s2 = "anica";
	char const* s3 = "lucas";
	auto m2 = ::max(s1, s2, s3); // 运行时错误（未定义的行为）
}
\end{lstlisting}

问题是，若对三个C字符串调用max()，

\begin{lstlisting}[style=styleCXX]
return max (max(a,b), c);
\end{lstlisting}

就会导致运行时错误，因为对于C字符，\texttt{max(a,b)}创建了一个新临时变量，并通过引用返回。但这个临时值越过返回语句完成就会销毁，留给\texttt{main()}一个悬空引用。不幸的是，这个错误非常微妙，可能不必现。

\begin{tcolorbox}[colback=webgreen!5!white,colframe=webgreen!75!black]
\hspace*{0.75cm}一般来说，符合标准的编译器可以编译这段代码。
\end{tcolorbox}

与此相反，\texttt{main()}中对\texttt{max()}的第一次调用不会出现相同的问题。虽为参数(7、42和68)创建了临时变量，但这些临时变量在\texttt{main()}中创建，\texttt{main()}中会一直存在，直到程序执行完毕。

这是由于重载解析规则,而导致行为与预期不同的代码示例。此外，需要在调用函数之前声明函数的所有重载版本。因为在进行相应的函数调用时，并非所有重载函数都可见(这很重要)。例如，定义三个参数版本的max()，而没有声明用于int类型的两个参数版本，就会导致使用三个参数版本的函数时，使用到两个参数的模板:

\hspace*{\fill} \\ %插入空行
\noindent
\textit{basics/max4.cpp}
\begin{lstlisting}[style=styleCXX]
#include <iostream>

// 两个任意类型的最大值:
template<typename T>
T max (T a, T b)
{
	std::cout << "max<T>() \n";
	return b < a ? a : b;
}

// 三个任意类型的最大值:
template<typename T>
T max (T a, T b, T c)
{
	return max (max(a,b), c); // 对整型也使用模板版本
}                             // 因为下面的声明来了太晚了
                              
// 两个int值的最大值:
int max (int a, int b)
{
	std::cout << "max(int,int) \n";
	return b < a ? a : b;
}

int main()
{
	::max(47,11,33); // OOPS: 这里使用的是max<T>()，而非max(int,int)
}
\end{lstlisting}

我们将在第13.2节中讨论其中细节。


